#include "fiber.h"
namespace sylar{
    static thread_local std::shared_ptr<Fiber> hauptfiber = nullptr; //主协程
    static thread_local Fiber* nowfiber = nullptr;//当前正在运行的协程
    static thread_local Fiber* shedulerfiber = nullptr;//调度协程，默认是主协程
    static std::atomic<uint64_t> fiber_id{0}; //id分配器
    static std::atomic<uint64_t> fiber_count {0}; //全局变量记录协程数目
    void Fiber::setthis(Fiber *f){ //设置当前运行的协程
        nowfiber = f;
    }
    std::shared_ptr<Fiber> Fiber::getthis(){
        if(nowfiber){
            return nowfiber->shared_from_this();
        }
        //如果nowfiber为空，那么意味着还没有创建主协程
        std::shared_ptr<Fiber> fiber(new Fiber());
        //设置调度协程和主协程
        hauptfiber = fiber;
        shedulerfiber = fiber.get();

        return nowfiber->shared_from_this();
    }
    Fiber::Fiber(){ //主协程的私有构造函数,因为
        //先设置当前运行的协程
        setthis(this);
        //初始化信息
        _state = READY;
        _id = fiber_id++;
        fiber_count++;
        if(getcontext(&_ctx) == -1){
            logmessage(FATAL,"ctx get faild by Fiber()");
            pthread_exit(nullptr);
        }
        logmessage(NORMAL,"主协程构造成功");
    }
    void Fiber::setshedulerfiber(Fiber* f){
        shedulerfiber = f;
    }
    uint64_t Fiber::getfiberid(){
        if(nowfiber){
            return nowfiber->getid();
        }
        return (uint64_t)-1;
    }

    Fiber::Fiber(func_t func,uint64_t stacksize,bool run_in_sheduler)
        :_func(func),_stacksize(stacksize),_if_run_in_sheduler(run_in_sheduler){
            _id = fiber_id++;
            fiber_count++;
            _stacksize = stacksize? stacksize : 128000;
            _stack = malloc(_stacksize);
            _state = READY;
            //获取协程上下文
            if(getcontext(&_ctx) == -1){
                logmessage(ERROR,"获取ctx失败");
                pthread_exit(nullptr);
            }
            _ctx.uc_stack.ss_size = _stacksize;
            _ctx.uc_stack.ss_sp = _stack;
            _ctx.uc_link = nullptr;
            makecontext(&_ctx,Fiber::mainfunc,0);//修改ctx的上下文为执行mainfunc，执行完后调用yied返回主协程

            logmessage(NORMAL,"子协程创建成功，协程id%d",_id);
    }
    Fiber::~Fiber(){
        fiber_count--;
        if(_stack){
            free(_stack);
        }
        logmessage(NORMAL,"子协程id:%d,清理成功",_id);
    }
    
    void Fiber::reset(func_t func){
        //将已经处于term状态的协程重新设置新的任务
  
        assert(_stack && _state == TERM);
        _func = func;
        _state = READY;
        if(getcontext(&_ctx) ==-1){
            logmessage(ERROR,"获取ctx失败");
            pthread_exit(nullptr);
        }
        _ctx.uc_stack.ss_size=_stacksize;
        _ctx.uc_stack.ss_sp = _stack;
        _ctx.uc_link = nullptr;
        makecontext(&_ctx,Fiber::mainfunc,0);
        logmessage(NORMAL,"reset成功");
    }
    void Fiber::resume(){
        //恢复协程，将协程状态由ready改为running
        _state = RUNNING;
        //根据if_in_ruuning决定是谁保存我现在的上下文信息,然后执行新现在协程的上下文
        if(_if_run_in_sheduler){
           setthis(this);
            if(swapcontext(&(shedulerfiber->_ctx),&_ctx) == -1){
                logmessage(ERROR,"协程恢复失败");
                pthread_exit(nullptr);
            }
        }else{
            setthis(this);
            if(swapcontext(&(hauptfiber->_ctx),&_ctx) == -1){
                logmessage(ERROR,"协程恢复失败");
                pthread_exit(nullptr);
            }
        }
    }
    void Fiber::yield(){
        //暂停协程，将协程状态由ruuing或者term而来
        assert(_state == TERM || _state == RUNNING);
        if(_state == RUNNING){
            
            _state == READY;
        }
        if (_if_run_in_sheduler){
            setthis(shedulerfiber);
            if(swapcontext(&_ctx,&(shedulerfiber->_ctx)) == -1){
                logmessage(ERROR,"协程暂停失败");
                pthread_exit(nullptr);
            }
        }else{
            setthis(hauptfiber.get());
            if(swapcontext(&_ctx,&(hauptfiber->_ctx)) == -1){
                logmessage(ERROR,"协程暂停失败");
                pthread_exit(nullptr);
            }
        }
    }
    void Fiber::mainfunc(){
        std::shared_ptr<Fiber> cur = getthis();//获取当前运行的协程
        cur->_func();//执行任务
        cur->_func = nullptr;
        cur->_state = TERM;
        //运行完毕，退出运行权
        Fiber* raw_ptr = cur.get();
        cur.reset();//减少引用计数
        
        raw_ptr->yield();//交出行使权
    }
}